#pragma once
#include "..\addons\ScreenQuad.h"
#include <string>
#include "..\utils\mtrand.h"
#include "..\utils\PlainTextReader.h"
//#include <vector>

namespace ds {

struct Particle {
	bool alive;
	float timer;
	float ttl;
	Vec3 position;
	Vec3 initialPosition;
	Vec3 velocity;
	float radialVelocity;
	Vec3 size;
	float angle;
	float rotationAngle;
	Rect textureRect;
	Color color;
	float radius;
	float angleVelocity;
	bool emitted;
};

typedef List<Particle> Particles;

// -------------------------------------
// ParticleEmitter
// -------------------------------------
class ParticleEmitter {

public:
	ParticleEmitter(uint32 counter,const Rect& textureRect,float ttl,float initialSize) 
		: m_Max(counter) , m_TextureRect(textureRect) , m_TTL(ttl) , m_Size(initialSize) {}
	virtual ~ParticleEmitter() {}
	virtual void createParticles(const Vec3& startPosition,Particles& particles) = 0;
	void setTextureRect(const Rect& rect) {
		m_TextureRect = rect;
	}
	void setTTL(float ttl) {
		m_TTL = ttl;
	}
	void setSize(float size) {
		m_Size = size;
	}
	void setMax(uint32 max) {
		m_Max = max;
	}
protected:
	Rect m_TextureRect;
	float m_TTL;
	float m_Size;	
	uint32 m_Max;
};

// ------------------------------------------------
// BoxEmitter
// ------------------------------------------------
class BoxEmitter : public ParticleEmitter {

public:
	BoxEmitter(uint32 width,uint32 height,uint32 counter,const Rect& textureRect,float ttl,float initialSize);
	void createParticles(const Vec3& startPosition,Particles& particles);
private:	
	uint32 m_Width;
	uint32 m_Height;
	MTRand_open rand;
};

// ------------------------------------------------
// RingEmitter
// ------------------------------------------------
class RingEmitter : public ParticleEmitter {

public:
	RingEmitter(float radius,uint32 counter,const Rect& textureRect,float ttl,float initialSize,const Color& color = Color(1.0f,1.0f,1.0f,1.0f),bool rotate = true);
	void createParticles(const Vec3& startPosition,Particles& particles);
	void setTextureRect(const Rect& rect) {
		m_TextureRect = rect;
	}
	void setRadius(float radius) {
		m_Radius = radius;
	}
	void setRotate(bool rotate) {
		m_Rotate = rotate;
	}
private:
	float m_Radius;
	Color m_Color;
	bool m_Rotate;
	MTRand_open rand;
};

// ------------------------------------------------
// PointEmitter
// ------------------------------------------------
class PointEmitter : public ParticleEmitter {

public:
	PointEmitter(uint32 counter,const Rect& textureRect,float ttl,float initialSize,const Color& color = Color(1.0f,1.0f,1.0f,1.0f),bool rotate = true);
	void createParticles(const Vec3& startPosition,Particles& particles);
	void setTextureRect(const Rect& rect) {
		m_TextureRect = rect;
	}
	void setRotate(bool rotate) {
		m_Rotate = rotate;
	}
	void setColor(const Color& color) {
		m_Color = color;
	}
private:
	Color m_Color;
	bool m_Rotate;
	MTRand_open rand;
};

// -------------------------------------
// ParticleAffector
// -------------------------------------
class ParticleAffector {

public:
	virtual void update(Particles& particles,float elapsed) = 0;
};

// -------------------------------------
// LifetimeAffector
// -------------------------------------
class LifetimeAffector : public ParticleAffector {

public:
	LifetimeAffector(float ttl) : m_TTL(ttl) {}
	void update(Particles& particles,float elapsed);
private:
	float m_TTL;
};

// -------------------------------------
// PositionAffector
// -------------------------------------
class PositionAffector : public ParticleAffector {

public:
	PositionAffector() {}
	void update(Particles& particles,float elapsed);
private:

};

// -------------------------------------
// RadialVelocityAffector
// -------------------------------------
class RadialVelocityAffector : public ParticleAffector {

public:
	RadialVelocityAffector(float velocity,float velocityDiff) : m_Velocity(velocity) , m_VelocityDiff(velocityDiff) {
		rand.seed(GetTickCount());
	}
	void update(Particles& particles,float elapsed);
private:
	float m_Velocity;
	float m_VelocityDiff;
	MTRand_open rand;
};

// -------------------------------------
// PositionAffector
// -------------------------------------
class WiggleAffector : public ParticleAffector {

public:
	WiggleAffector(float min,float max,float amplitude) : m_Min(min) , m_Max(max) , m_Amplitude(amplitude) {	
		rand.seed(GetTickCount());
	}
	void update(Particles& particles,float elapsed);
private:
	float m_Min;
	float m_Max;
	float m_Amplitude;
	MTRand_open rand;
};
// -------------------------------------
// AlphaAffector
// -------------------------------------
class AlphaAffector : public ParticleAffector {

public:
	AlphaAffector(float min,float max) : m_Min(min) , m_Max(max) {}
	void update(Particles& particles,float elapsed);
private:
	float m_Min;
	float m_Max;
};

// -------------------------------------
// ColorFadeAffector
// -------------------------------------
class ColorFadeAffector : public ParticleAffector {

public:
	ColorFadeAffector(float rmin,float rmax,float gmin,float gmax,float bmin,float bmax) 
		: m_rMin(rmin) , m_rMax(rmax) , m_gMin(gmin) , m_gMax(gmax), m_bMin(bmin) , m_bMax(bmax) {}
	void update(Particles& particles,float elapsed);
private:
	float m_rMin;
	float m_rMax;
	float m_gMin;
	float m_gMax;
	float m_bMin;
	float m_bMax;
};

// -------------------------------------
// SizeAffector
// -------------------------------------
class SizeAffector : public ParticleAffector {

public:
	SizeAffector(float min,float max) : m_Min(min) , m_Max(max) {}
	void update(Particles& particles,float elapsed);
private:
	float m_Min;
	float m_Max;
};

// -------------------------------------
// RotationAffector
// -------------------------------------
class RotationAffector : public ParticleAffector {

public:
	RotationAffector(float frequency) : m_Frequency(frequency) {}
	void update(Particles& particles,float elapsed);
private:	
	float m_Frequency;
};

// -------------------------------------
// SizeSineAffector
// -------------------------------------
class SizeSineAffector : public ParticleAffector {

public:
	SizeSineAffector(float min,float multiplier,float amplitude) : m_Min(min) , m_Multiplier(multiplier) , m_Amplitude(amplitude) {}
	void update(Particles& particles,float elapsed);
private:
	float m_Min;
	float m_Multiplier;
	float m_Amplitude;
};

// -------------------------------------
// ParticleSystem
// -------------------------------------
class ParticleSystem {

typedef List<ParticleAffector*> Affectors;

public:
	ParticleSystem(const std::string& name);
	~ParticleSystem(void);
	void update( float elapsed );
	void setEmitter(ParticleEmitter* emitter,bool external = false) {
		m_Emitter = emitter;
		m_ExternalEmitter = external;
	}
	ParticleEmitter* getEmitter() {
		return m_Emitter;
	}
	void addAffector(ParticleAffector* affector) {
		m_Affectors.append(affector);
	}
	void start(const Vec3& startPosition);
	void fillBuffer(ColouredScreenQuad* particleBuffer);
	bool isAlive() {
		return m_Particles.num() > 0;
	}
private:
	bool m_ExternalEmitter;
	std::string m_Name;
	Affectors m_Affectors;
	ParticleEmitter* m_Emitter;
	Particles m_Particles;
};

class ParticleSystemDecl;

// -------------------------------------
// ParticleManager
// -------------------------------------
class ParticleManager : public ColouredScreenQuad {

typedef List<ParticleSystem*> Systems;

public:
	ParticleManager(const std::string& name,const std::string& textureName,const std::string& layerName);
	virtual ~ParticleManager() {}
	ResourceDeclaration* parseCategory(Category* root);
	void loadContent( ResManager& resourceManager );
	void update( float elapsed );	
	ParticleSystem* prepare(const std::string& systemName);
	void start(const Vec3& startPosition,ParticleSystem* particleSystem);
	void start(const Vec3& startPosition,const std::string& systemName);
private:
	std::string m_Name;
	std::string m_TextureName;
	std::string m_LayerName;	
	Systems m_Systems;
};

}
